-- $Id: $
-- File name:   OUTPUT_BLOCK.vhd
-- Created:     4/6/2011
-- Author:      Brandon Blaine Gardner
-- Lab Section: 337-06
-- Version:     1.0  Initial Design Entry
-- Description: The output block for the HD Audio Controller

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_arith.ALL;
USE IEEE.std_logic_unsigned.ALL;

entity OUTPUT_BLOCK is
	port(
		CLK : in  std_logic;
		RST  : in  std_logic;
		EN   : in  std_logic;
		SYNC : in  std_logic;
		STB  : in  std_logic;
		DATA : in  std_logic_vector (15 downto 0);
		REQ  : out std_logic;
		SDO  : out std_logic
	);
end OUTPUT_BLOCK;

architecture sdo_arch of OUTPUT_BLOCK is
	
	-- operational state machine signals
	type state_type is ( INIT, Waiting, Out0, Out1 );
  signal state, nextstate : state_type;
  
  -- data input state machine signals
  type indata_type is ( IDLE, Request, Strobe );
  signal indata, nextindata : indata_type;
  signal prevstate : state_type;
  
  -- output and counter signals
  signal sdo_int : std_logic;
  signal sdo_int_f : std_logic;
  signal bitcount, nextbitcount : std_logic_vector(3 downto 0); -- 4 bits
  signal wordcount, nextwordcount : std_logic_vector(5 downto 0); -- 6 bits

	component SET_N_SHIFT_16
		port(
			CLK	         : in  std_logic;
      RST_N        : in  std_logic;
    	 SHIFT_ENABLE : in  std_logic; -- enable for shifting out
    	 SET_ENABLE   : in  std_logic; -- pulse for setting value
    	 DATA_IN      : in std_logic_vector(15 downto 0);
    	 SHIFT_OUT    : out std_logic
    );
	end component;
	
	-- set-n-shift signals
	signal sns0_shift, sns1_shift : std_logic; -- set-n-shift internal shift enable
	signal sns0_set, sns1_set : std_logic; -- set-n-shift internal set enable
	signal sns0_out, sns1_out : std_logic; -- set-n-shift internal output
	
begin

	SDO <= sdo_int_f;

------------------------------------------------------------------------

	SNS_0 : SET_N_SHIFT_16 port map(
		CLK => CLK,
		RST_N => RST,
		SHIFT_ENABLE => sns0_shift,
		SET_ENABLE => sns0_set,
		DATA_IN => DATA,
		SHIFT_OUT => sns0_out
	);
	
	SNS_1 : SET_N_SHIFT_16 port map(
		CLK => CLK,
		RST_N => RST,
		SHIFT_ENABLE => sns1_shift,
		SET_ENABLE => sns1_set,
		DATA_IN => DATA,
		SHIFT_OUT => sns1_out
	);

------------------------------------------------------------------------
	
	state_reg : process( CLK, RST )
	begin
		if( RST = '0' )
		then
			state <= INIT;
			bitcount <= "0000";
			wordcount <= "000000";
			sdo_int_f <= '0';
		elsif( rising_edge(CLK) )
		then
			state <= nextstate;
			bitcount <= nextbitcount;
			wordcount <= nextwordcount;
			sdo_int_f <= sdo_int;
		end if;
	end process state_reg;
	
	nextstate_logic : process( state,
				bitcount, wordcount, sns0_out, sns1_out,
				nextbitcount, nextwordcount, EN, SYNC, sdo_int_f )
	begin
		
		nextstate <= state;
		nextbitcount <= bitcount;
		nextwordcount <= wordcount;
		sdo_int <= '0';
		sns0_shift <= '0';
		sns1_shift <= '0';
		sdo_int <= sdo_int_f;
		
		case state is
		
		when INIT =>
			if( EN = '1' )
			then
				nextstate <= Waiting;
			end if;
			
		when Waiting =>
			if( EN = '0' )
			then
				nextstate <= INIT;
			elsif( SYNC = '1' )
      then
			  nextbitcount <= bitcount + 1;
        if( bitcount = "0111" )
        then
          nextbitcount <= "0000";
          nextstate <= Out0;
        end if;
      else
        nextbitcount <= "0000";
			end if;
      
		when Out0 =>
			sns0_shift <= '1';
			sdo_int <=  sns0_out;
			if( EN = '0' )
			then
				nextstate <= INIT;
			elsif( bitcount = "1111" )
			then
				nextbitcount <= "0000";
				nextwordcount <= wordcount + 1;
				if( wordcount = "101010" ) -- if current word count is 41 ***** was 42 (change for good?)
				then
					nextstate <= Waiting;
					nextwordcount <= "000000";
				else
					nextstate <= Out1;
				end if;
			else
				nextbitcount <= bitcount + 1;
			end if;
			
		when Out1 =>
			sns1_shift <= '1';
			sdo_int <= sns1_out;
			if( EN = '0' )
			then
				nextstate <= INIT;
			elsif( bitcount = "1111" )
			then
				nextbitcount <= "0000";
				nextwordcount <= wordcount + 1;
				if( wordcount = "101010" ) -- if current word count is 41 ***** was 42 (change for good?)
				then
					nextstate <= Waiting;
					nextwordcount <= "000000";
				else
					nextstate <= Out0;
				end if;
			else
				nextbitcount <= bitcount + 1;
			end if;
			
		end case;
			
	end process nextstate_logic;
			
------------------------------------------------------------------------

	indata_reg : process( CLK, RST, state )
	begin
		if( RST = '0' )
		then
			 indata <= Request;
			 prevstate <= INIT;
		elsif( rising_edge(CLK) )
		then
			indata <= nextindata;
			prevstate <= state;
		end if;
	end process indata_reg;
	
	indata_logic : process( indata, state, prevstate, STB )
	
	begin
		
		nextindata <= indata;
		sns0_set <= '0';
		sns1_set <= '0';
		REQ <= '0';
		
		case indata is
		
		when IDLE =>
			--nextindata <= IDLE;
			if( not(state = prevstate) and not(prevstate = INIT) ) -- on state switch
			then
				nextindata <= Request;
			end if;
			
		when Request =>
			REQ <= '1';
			if( STB = '1' )
			then
				nextindata <= Strobe;
			end if;
			
		when Strobe =>
			if( state = Out0 )
			then
				sns1_set <= '1';
			else
				sns0_set <= '1';
			end if;
			nextindata <= IDLE;
			
		end case;
			
	end process indata_logic;

end sdo_arch;
